// OS.cpp
// Contains code to detect OS and system
#include "os.hpp"
#include "errors.hpp"

#include <vector>
#include <dirent.h>
#include <sys/stat.h>
#include <fstream>
#include <cstring>

using namespace std;

#ifdef USING_WINDOWS
int MakeDir(const char* path) {
	return _mkdir(path);
}
int CurrentWorkingDir(char* &buffer) {
	buffer = _getcwd(NULL, 0);
	return (buffer != NULL);
}
int isDir(const char* path) {
	struct _stat fileinfo;
	_stat(path, &fileinfo);
	if (_S_ISDIR(fileinfo.st_mode)) {
		return 1;
	} else if (_S_ISREG(fileinfo.st_mode)) {
		return 0;
	} else {
		return 2;
	}
}
int ChangeDir(const char* path) {
	return chdir(path);
}
int Remove(const char* path) {
	if (isDir(path) == 0) {
		if (remove(path) != 0) {
			return 1;
		} else {
			return 0;
		}
	} else {
		if (rmdir(path) != 0) {
			return 1;
		} else {
			return 0;
		}
	}	
}
#endif

#ifdef USING_LINUX
int MakeDir(const char* path) {
	return mkdir(path, S_IRWXU);
}
int CurrentWorkingDir(char* &buffer) {
	buffer = getcwd(NULL, 0);
	return (buffer != NULL);
}
int isDir(const char* path) {
	struct stat fileinfo;
	stat(path, &fileinfo);
	if (S_ISDIR(fileinfo.st_mode)) {
		cout << "Object named:\n\t`" << path << "`\nIs a: \tFolder\n";
		return 1;
	} else if (S_ISREG(fileinfo.st_mode)) {
		cout << "Object named:\n\t`" << path << "`\nIs a: \tFile\n";
		return 0;
	} else {
		cout << "Object named:\n\t`" << path << "`\nIs a: \tWeird file\n";
		return 2;
	}
}

int ChangeDir(const char* path) {
	return chdir(path);
}

int Remove(const char* path) {
	if (isDir(path) == 0) {
		if (remove(path) != 0) {
			return 1;
		} else {
			return 0;
		}
	} else {
		if (rmdir(path) != 0) {
			return 1;
		} else {
			return 0;
		}
	}	
}
#endif
int Copy(const char* path, const char* destpath) {
	ifstream file(path, fstream::binary);
	if(file.is_open()) {
		ofstream destfile(destpath, fstream::trunc|fstream::binary);
		if(destfile.is_open()) {
			destfile << file.rdbuf();
		} else {
			return 1;
		}
	} else {
		return 1;
	}
	return 0;
}

int Move(const char* path, const char* destpath) {
	Copy(path, destpath);
	Remove(path);
	return 0;
}

vector<pair<string,short> >* DirectoryListing(const char* path, std::string arg) {
	DIR *directory;
	struct dirent *fileinfo;
	// Only one instance is ever created
	vector<pair<string, short> >* DirContents = new vector<pair<string,short> >;
	// The instance is cleared each time
	pair<const char*, short> files;
	directory = opendir(path);
	if (directory != NULL) {
		while ((fileinfo = readdir(directory))) {
			//a is not found in the argument list
			if(arg.find("a") == string::npos) {
				//the filename doesn't start with a . then continue
				if(fileinfo->d_name[0] == '.')  {
					continue;
				}
			}
			string pathisdir = path;
			pathisdir += PATHSEPARATOR;
			pathisdir += fileinfo->d_name;
			files = make_pair(fileinfo->d_name, isDir(pathisdir.c_str()));
			DirContents->push_back(files);
		}
	}
	return DirContents;
}


int RecursiveFileIO(string path, FileOp Func, string destpath) {
	switch (Func) {
		case opDelete:
			break;
		case opCopy:
			MakeDir(destpath.c_str());
			break;
		case opMove:
			MakeDir(destpath.c_str());
			break;
	}
	pair<string, short> file;
	vector<pair<string, short> >* Dirlist = DirectoryListing(path.c_str());
	for(unsigned int i=Dirlist->size(); i>0; i--) {
		file = Dirlist->back();
		Dirlist->pop_back();
		string filepath = file.first;
		if(destpath[destpath.size()-1] != PATHSEPARATOR ) {
			destpath +=  PATHSEPARATOR;
		}
		if((file.second == 1) && (filepath[filepath.size()-1] != PATHSEPARATOR)) {
			filepath += PATHSEPARATOR;	
		}
		//Create string that contains file path
		string tmp_path = path;
		tmp_path += PATHSEPARATOR;
		tmp_path += filepath;
		//Create string that contains destination path
		string tmp_dest = destpath;
		tmp_dest += filepath;
		switch (file.second) {
			//Path leads to regular file
			case 0:
				switch (Func)
				{
					case opDelete:
						Remove(tmp_path.c_str());
						break;
					case opCopy:
						Copy(tmp_path.c_str(), tmp_dest.c_str());
						break;
					case opMove:
						Move(tmp_path.c_str(), tmp_dest.c_str());
						break;
					default:
						delete Dirlist;
						return 1;
				}
				break;
			//Path leads to Directory
			case 1:
				switch (Func) {
					case opDelete:
						RecursiveFileIO(tmp_path.c_str(), Func, tmp_dest.c_str());
						Remove(tmp_path.c_str());
					case opCopy:
						MakeDir(tmp_dest.c_str());
						RecursiveFileIO(tmp_path.c_str(), Func, tmp_dest.c_str());
						break;
					case opMove:
						MakeDir(tmp_path.c_str());
						RecursiveFileIO(tmp_path.c_str(), Func, tmp_dest.c_str());
						Remove(tmp_dest.c_str());
				}
				break;
			//Path leads to other filetype
			case 2:
				delete Dirlist;
				return 1;
		}
	}	
	switch (Func) {
		case opDelete: 
			Remove(path.c_str());
		case opMove:
			Remove(path.c_str());
		default:
			break;
	}
	delete Dirlist;
	return 0;
}
